import {extend, hasChanged, hasOwn, isArray, isInteger, isObject} from './utils';
import {reactive,readonly} from './reactive';
import {track,trigger} from './effect';
import {TrackGet, TrackSet, TriggerOrAdd, TriggerOrSet} from './operators'


//! 实现 new proxy(target,handle)

//! -----------------------get()-------------------------
// 不仅读 不浅的
const get = createGetter();
// 不仅读 浅的
const shallowGet = createGetter(false,true);
// 仅读
const readonlyGet = createGetter(true);
// 仅读 浅的
const shallowReadonlyGet = createGetter(true,true);

// 是不是仅读 是不是浅的  拦截获取功能
function createGetter(isReadonly=false,shallow=false) {
   return function get(target,key,receiver) { // receiver：代理对象本身
      // proxy + reflect 反射  意思：来proxy取值，我就把原来目标target的值反射回去
      // 以前targe[key] = value  设置值有可能会失败  但是没有结果响应标识
      // Reflect方法有返回值 告诉设置成功没有
      // 这个只代理了  第一层
      const res = Reflect.get(target,key,receiver);

      //! 收集依赖，等数据变化后，更新对应的视图  如果 不仅读
      if (!isReadonly) {
         // 执行effect时会取值，收集effect
         // 依赖收集方法 意思：我要对target这个对象的key属性进行取值（TrackGet）的时候进行收集
         track(target,TrackGet,key);
      }
      // 如果是 浅的 直接返回反射对象
      if (shallow) {
         return res;
      }
      // 如果结果是对象 就是深的 就还要判断是不是仅读 然后递归处理
      // vue2一上来就递归代理，vue3是取值时进行代理，vue3的叫做懒代理
      if (isObject(res)) {
         // 如果不仅读，就reactive深层代理
         return isReadonly ? readonly(res) : reactive(res);
      }
      return res;
   }
}
//! ---------------------set()-----------------------------
// 仅读的话 set()
const readonlySet = {
   set: (target,key) => { // 仅读 没有set()
      console.warn(`设置 ${key} 错误,因为该属性为仅读`);
   }
}
// 不仅读的 set() 是不是 浅的
const set = createSetter();
const shallowSet = createSetter(true);

// 只判断是不是 浅的    拦截设置功能
function createSetter(Shallow=false) {
   return function set(target,key,value,receiver) {
      // 当数据更新时，通知对应的属性的effect函数重新执行
      // 我们要区分是新增  还是修改 vue2 无法监控索引更改，数组长度变化  vue3就可以  hack方法要特殊处理
      const oldValue = target[key]; // 获取老值
      // 如果是数组 并且修改数组的索引 ==> 我就看下你改的值是不是当前数组长度以内的：：如果不是=》那就说明新增
      // hadKey 有没有这个属性  这样就可以判断 新增还是修改  修改hadKey=true  新增hadKey=false
      let hadKey = isArray(target) && isInteger(key) ? Number(key) < target.length : hasOwn(target,key);

      // set也一样  proxy + reflect 反射  意思：来proxy取值，我就把原来目标target的值反射回去
      // 以前targe[key] = value  设置值有可能会失败  但是没有结果响应标识
      // Reflect方法有返回值 告诉设置成功没有
      const result = Reflect.set(target,key,value,receiver);

      // 这里我们就看下新增 修改处理
      if (!hadKey) { // 新增
         // 触发更新 TriggerOrAdd  新增
         trigger(target,TriggerOrAdd,key,value);
      }else if (hasChanged(oldValue, value)) { // 修改
         // 触发更新  为修改 把老值也传入
         trigger(target,TriggerOrSet,key,value,oldValue);
      }

      return result;
   }
}

//! 实现 new proxy(target,handle)
// 考虑： 是不是仅读  仅读set()  会报异常
// 考虑： 是不是深度的
export const mutableHandlers = {
   get,
   set
}
// 浅的  只代理第一层
export const shallowReactiveHandlers = {
   get: shallowGet,
   set: shallowSet
}
// 仅读的
export const readonlyHandlers = extend({ // extend合并对象
   get: readonlyGet
},readonlySet);
// 浅的  仅读的  浅的：仅第一层不能改，里面的可以改
export const shallowReadonlyHandlers = extend({
   get: shallowReadonlyGet
},readonlyGet);